#include "Checkers.h"

Checkers::Checkers(void)
:status(STATUS_IN_PROGRESS),movesWithNoCapture(0), whoseTurnIsIt(CHECKERS_PLAYER1),pathFinderHolder(0)
{
	gameBoard = new Board();

	// Player 2 at the top, 1 at the bottom
	// Start stacking in the users lower left corner (which is the dark one)
	int placedPieces = 0;
	int xPosition;
	int yPosition;

	// Do player 1
	xPosition = BOARD_X-1;
	for( yPosition=0; yPosition<BOARD_Y && placedPieces < PLAYER_PIECES; yPosition++)
	{
		while( xPosition >= 0 )
		{
			gameBoard->SetValue(xPosition,yPosition,CHECKERS_PLAYER2);
			placedPieces++;
			xPosition -= 2;
		}
		xPosition = BOARD_X - (1+ (yPosition+1)%2);
	}

	// Do player 2
	xPosition = 0;
	placedPieces = 0;
	for( yPosition=(BOARD_Y-1); yPosition>0 && placedPieces < PLAYER_PIECES; yPosition--)
	{
		while( xPosition < BOARD_X )
		{
			gameBoard->SetValue(xPosition,yPosition,CHECKERS_PLAYER1);
			placedPieces++;
			xPosition += 2;
		}
		xPosition = yPosition%2;
	}
}

Checkers::Checkers(int currentStatus, int player, Board* board)
:status(currentStatus),movesWithNoCapture(0),whoseTurnIsIt(player),pathFinderHolder(0)
{
	gameBoard = new Board();

	(*gameBoard) = (*board);
}


Checkers::~Checkers(void)
{
	if( gameBoard )
	{
		delete gameBoard;
		gameBoard = 0;
	}

	if( pathFinderHolder )
	{
		delete pathFinderHolder;
		pathFinderHolder = 0;
	}
}

vector<Path*> Checkers::GetMoves()
{
	if( pathFinderHolder )
		delete pathFinderHolder;

	pathFinderHolder = new AIPathFinder(GetCurrentPlayer(),GetBoard());
	return pathFinderHolder->GetPaths();
}

// Replaces IsMoveLegal(), if it's legal, the path is returned, otherwise it's null (0)
Path* Checkers::FindPathForMove(int x1, int y1, int x2, int y2)
{
	if( pathFinderHolder )
		delete pathFinderHolder;

	pathFinderHolder = new AIPathFinder(GetCurrentPlayer(),GetBoard());
	vector<Path*> paths = pathFinderHolder->GetPaths();

	// Try and find a path starting at x1,x2 and ending at x2,y2
	for(size_t idx = 0; idx < paths.size(); idx++)
	{
		Path* curPath = dynamic_cast<Path*>(paths[idx]);
		if( curPath &&
			curPath->GetPosition().GetX() == x1 &&
			curPath->GetPosition().GetY() == y1 )
		{
			// check the points along this path to see if we can get there....
			for( size_t idxPosition = 0; idxPosition < curPath->GetPath().size(); idxPosition++)
			{
				PiecePosition* position = dynamic_cast<PiecePosition*>(curPath->GetPath()[idx]);
				if( position &&
					position->GetX() == x2 &&
					position->GetY() == y2)
				{
					return curPath;
				}
			}
		}
	}

	return 0;
}

Path* Checkers::GetBestPath()
{
	if( pathFinderHolder )
		delete pathFinderHolder;

	pathFinderHolder = new AIPathFinder(GetCurrentPlayer(),GetBoard());
	vector<Path*> paths = pathFinderHolder->GetPaths();

	if( paths.size() > 0 )
		return dynamic_cast<Path*>(paths[0]);
	
	// This guy has no moves....force a change of turn ownership
	ChangePlayerTurn();
	return 0;
}

bool Checkers::FollowPath(Path* path)
{
	int x1, y1, x2, y2;

	x1 = path->GetPosition().GetX();
	y1 = path->GetPosition().GetY();

	for( size_t i = 0; i < path->GetPath().size(); i++)
	{
		PiecePosition* nextPosition = dynamic_cast<PiecePosition*>(path->GetPath()[i]);
		x2 = nextPosition->GetX();
		y2 = nextPosition->GetY();

		if( Move(x1,y1,x2,y2) == MOVE_FAIL)
			return false;

		x1 = x2;
		y1 = y2;
	}

	return true;
}


int Checkers::Move(int x1, int y1, int x2, int y2)
{
	if( !IsIndividualMoveLegal(x1,y1,x2,y2) )
		return MOVE_FAIL;

	// Up the count of moves with no capture (reset if there is one)
	movesWithNoCapture++;

	// Prepare a return code
	int returnCode = MOVE_OK;

	// Get what's currently there...
	int currentValue = gameBoard->GetValue(x1,y1);

	// Get the distance to travel
	int distanceX = x2-x1;
	int distanceY = y2-y1;

	// Clear the starting point
	gameBoard->SetValue(x1,y1,CHECKERS_EMPTY);

	// If we have a capture, clear it as well
	if( abs(distanceX) > 1)
	{
		// Clear out the count for a draw
		movesWithNoCapture = 0;

		// Set the capture position
		postCapturePosition.Set(x2,y2);

		gameBoard->SetValue(x1 + (distanceX/2), y1 + (distanceY/2),CHECKERS_EMPTY);

		// Does this capture end the game?
		if( gameBoard->GetPlayer1PieceCount() == 0 )
			status = STATUS_PLAYER2_WON;
		else if( gameBoard->GetPlayer2PieceCount() == 0 )
			status = STATUS_PLAYER1_WON;

		returnCode = MOVE_OK_CAPTURE;
	}
	else
	{
		// No capture and we can clear the postCapturePosition
		postCapturePosition.Clear();
	}

	// Place the moved player
	gameBoard->SetValue(x2,y2,currentValue);

	// If we are moving a player to kingship, make it so here
	if( (y2 == (BOARD_Y -1)) && 
		currentValue == CHECKERS_PLAYER2)
	{
		gameBoard->SetValue(x2,y2,CHECKERS_PLAYER2_KING);
	}
	else if( (y2 == 0 ) &&
			  currentValue == CHECKERS_PLAYER1)
	{
		gameBoard->SetValue(x2,y2,CHECKERS_PLAYER1_KING);
	}

	// If we have 50 moves with no captures, then it's a draw
	if( movesWithNoCapture >= 50)
		status = STATUS_DRAW;

	// If the move was OK then change turns. If it was a capture, and we
	// can't capture with that single piece again, then change turns.
	if( returnCode == MOVE_OK )
	{
		ChangePlayerTurn();
	}
	else if( !CanPerformAnotherCapture() )
	{
		postCapturePosition.Clear();
		ChangePlayerTurn();
	}

	return returnCode;
}

bool Checkers::IsIndividualMoveLegal(int x1, int y1, int x2, int y2)
{
	// Check the points, all of them must exist inside the board dimensions.
	if( x1 < 0 || x2 < 0 ||
		x1 > (BOARD_X -1) ||  x1 > (BOARD_X -1) ||
		y1 < 0 || y2 < 0 ||
		y1 > (BOARD_Y-1) || y2 > (BOARD_Y-1))
	{
		return false;
	}

	// Collect the values in the current squares
	int currentValue = gameBoard->GetValue(x1,y1);
	int targetValue = gameBoard->GetValue(x2,y2);

	// Validate that the right player can make this move
	if( !CorrectPlayerPieceSelection(currentValue) ||
		!ValidatePostCaptureStatus(x1,y1))
	{
		return false;
	}

	// The move must be :
	//	1. symmetrical x==y distances. 
	//	2. Can move 1 or 2 spaces only (2 assuming a capture)
	//	3. If last move was a capture then this move MUST be a capture, distance 2, we'll figure out
	//	   if the capture is valid later.
	int distanceX = x2-x1;
	int distanceY = y2-y1;

	if( (abs(distanceX) != abs(distanceY)) ||
		( (abs(distanceX) != 1)&&(abs(distanceX)!=2) ) ||
		( postCapturePosition.IsSet() && abs(distanceX) == 1) )
	{
		return false;
	}

	// For player 2 (top of screen) the Y component of the move to space has to
	// be less than the starting space. For player one, it's reversed.
	if( currentValue == CHECKERS_EMPTY ||
		targetValue != CHECKERS_EMPTY)
	{
		return false;
	}
	else if( currentValue == CHECKERS_PLAYER2)
	{
		// Move has to be down...
		if( y2 <= y1 )
			return false;
	}
	else if( currentValue == CHECKERS_PLAYER1)
	{
		// Move has to be up...
		if( y2 >= y1 )
			return false;
	}
	// The final check here would have been for king, but we know they can go anywhere
	// so no need to verify the y-axis move direction.....

	// If the distance is >1 (i.e. 2) then we are jumping, have to validate that the 
	// guy in-between is of the opposing team.
	if( abs(distanceX) > 1 )
	{
		int jumpTarget = gameBoard->GetValue(x1 + (distanceX/2), y1 + (distanceY/2));

		// Can't jump you're own team-mate
		if( jumpTarget == CHECKERS_EMPTY)
		{
			return false;
		}
		if( (currentValue == CHECKERS_PLAYER1 || currentValue == CHECKERS_PLAYER1_KING) &&
			(jumpTarget == CHECKERS_PLAYER1 || jumpTarget == CHECKERS_PLAYER1))
		{
			return false;
		}
		else if( (currentValue == CHECKERS_PLAYER2 || currentValue == CHECKERS_PLAYER2_KING) &&
			(jumpTarget == CHECKERS_PLAYER2 || jumpTarget == CHECKERS_PLAYER2))
		{
			return false;
		}
	}

	return true;
}

Board* Checkers::GetBoard()
{
	return gameBoard;
}

int Checkers::GetStatus()
{
	return status;
}

void Checkers::ChangePlayerTurn()
{
	whoseTurnIsIt = (whoseTurnIsIt==CHECKERS_PLAYER1?CHECKERS_PLAYER2:CHECKERS_PLAYER1);
}

bool Checkers::CorrectPlayerPieceSelection(int boardValue)
{
	if( whoseTurnIsIt == CHECKERS_PLAYER1 )
	{
		return (boardValue == CHECKERS_PLAYER1 || boardValue == CHECKERS_PLAYER1_KING);
	}
	else
	{
		return (boardValue == CHECKERS_PLAYER2 || boardValue == CHECKERS_PLAYER2_KING);
	}
}

bool Checkers::ValidatePostCaptureStatus(int x, int y)
{
	if( postCapturePosition.IsSet() )
	{
		return (postCapturePosition.GetX() == x && postCapturePosition.GetY() == y);
	}

	// if not set this is ALWAYS true
	return true;
}

bool Checkers::CanPerformAnotherCapture()
{
	if( !postCapturePosition.IsSet() )
		return false;

	int x = postCapturePosition.GetX();
	int y = postCapturePosition.GetY();

	return CanPerformCapture(x,y);
}

bool Checkers::CanPerformCapture(int x, int y)
{
	if( IsIndividualMoveLegal(x,y,x-2,y-2) ||
		IsIndividualMoveLegal(x,y,x-2,y+2) ||
		IsIndividualMoveLegal(x,y,x+2,y-2) ||
		IsIndividualMoveLegal(x,y,x+2,y+2))
	{
		return true;
	}

	return false;
}


bool Checkers::CanMove(int x, int y)
{
	if( IsIndividualMoveLegal(x,y,x-1,y-1) ||
		IsIndividualMoveLegal(x,y,x-1,y+1) ||
		IsIndividualMoveLegal(x,y,x+1,y-1) ||
		IsIndividualMoveLegal(x,y,x+1,y+1))
	{
		return true;
	}

	return false;
}

void Checkers::CollectCaptures(int x, int y, vector<MoveData*> &legalMoves)
{
	if( IsIndividualMoveLegal(x,y,x-2,y-2) )
	{
		legalMoves.push_back(new MoveData(x,y,x-2,y-2));
	}
	if( IsIndividualMoveLegal(x,y,x-2,y+2) )
	{
		legalMoves.push_back(new MoveData(x,y,x-2,y+2));
	}
	if( IsIndividualMoveLegal(x,y,x+2,y-2) )
	{
		legalMoves.push_back(new MoveData(x,y,x+2,y-2));
	}
	if(IsIndividualMoveLegal(x,y,x+2,y+2) )			
	{
		legalMoves.push_back(new MoveData(x,y,x+2,y+2));
	}
}

void Checkers::CollectMoves(int x, int y, vector<MoveData*> &legalMoves)
{
	if( IsIndividualMoveLegal(x,y,x-1,y-1) )
	{
		legalMoves.push_back(new MoveData(x,y,x-1,y-1));
	}
	if( IsIndividualMoveLegal(x,y,x-1,y+1) )
	{
		legalMoves.push_back(new MoveData(x,y,x-1,y+1));
	}
	if( IsIndividualMoveLegal(x,y,x+1,y-1) )
	{
		legalMoves.push_back(new MoveData(x,y,x+1,y-1));
	}
	if(IsIndividualMoveLegal(x,y,x+1,y+1) )			
	{
		legalMoves.push_back(new MoveData(x,y,x+1,y+1));
	}
}


// Really, two cases:
//	1. A jump has occured and that is the ONLY valid movable piece.
//	2. A standard turn:
//		- If any jumps are available, they are the only ones returned
//		- If no jumps available, any possible move is returned.
int Checkers::GetLegalMoves(vector<MoveData*> &legalMoves)
{
	// Clear it out....
	ClearLegalMoves(legalMoves);

	if( CanPerformAnotherCapture() )
	{
		CollectCaptures(postCapturePosition.GetX(),
						postCapturePosition.GetY(),
						legalMoves);
	}
	else
	{
		bool capturesAvailable = false;
		vector<PiecePosition*> positionList;

		// Get a list of ALL of the positions for the current player and figure out 
		// if any captures are available. 
		for( int yPos=0; yPos < BOARD_Y; yPos++)
		{
			for( int xPos=0; xPos < BOARD_X; xPos++)
			{
				if( CorrectPlayerPieceSelection(gameBoard->GetValue(xPos,yPos)))
				{
					if( CanPerformCapture(xPos,yPos))
						capturesAvailable = true;

					PiecePosition* pos = new PiecePosition(xPos,yPos);
					positionList.push_back(pos);
				}
			}
		}

		// Now collect the data for the legal moves, if any

		for( size_t i = 0; i < positionList.size(); i++)
		{
			PiecePosition* pp = dynamic_cast<PiecePosition*>(positionList[i]);
			if( pp != 0 )
			{
				int x = pp->GetX();
				int y = pp->GetY();
				delete pp;

				// use it and delete it....
				if( capturesAvailable)
				{
					CollectCaptures(x,y,legalMoves);
				}
				else
				{
					CollectMoves(x,y,legalMoves);
				}
			}
		}

		positionList.clear();
	}

	return (int)legalMoves.size();
}

// Clears the vector of moves that were returned by the GetLegalMoves call
void Checkers::ClearLegalMoves(vector<MoveData*> & legalMoves)
{
	for( size_t i = 0; i < legalMoves.size(); i++)
	{
		MoveData* md = dynamic_cast<MoveData*>(legalMoves[i]);
		if( md != 0 )
		{
			delete md;
		}
	}

	legalMoves.clear();
}
